﻿
namespace Patterns4Net.Tests.PatternEnforcer.Core.Patterns.Regression
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Mono.Cecil;
    using NUnit.Framework;
    using Patterns4Net.Attributes;
    using Patterns4Net.Core.CecilExtensions;
    using Patterns4Net.PatternEnforcer.Core.Patterns;

    public class PatternCheckerTestBase
    {
        protected void TestChecker(IPatternChecker checker, string elementName, IPattern pattern, bool expectedResult) 
        {
            Contract.Requires(checker != null);
            Contract.Requires(pattern != null);

            var result = checker.Check(pattern);
            var assertMessage = string.Format(
                "For element '{0}' and pattern {1}, the checker should have returned CheckerResult.IsValid == {2}.",
                elementName,
                pattern.Name,
                expectedResult);
            Assert.AreEqual(expectedResult, result.IsValid, assertMessage);
        }

        protected void TestChecker(IPatternChecker checker, Type type, IPattern pattern)
        {
            Contract.Requires(checker != null);
            Contract.Requires(type != null);

            var attribute = (PatternTestAttribute)Attribute.GetCustomAttribute(type, typeof(PatternTestAttribute));
            this.TestChecker(checker, type.Name, pattern, attribute.ExpectedResult);
        }

        protected void TestChecker(IPatternChecker checker, Type type, string methodName, Func<MethodDefinition, IPattern> pattern)
        {
            Contract.Requires(checker != null);
            Contract.Requires(type != null);

            var methodInfo = type.GetMethod(methodName);
            Contract.Assume(methodInfo != null, string.Format("Method with name {0} not found in type {1}.", methodName, type.Name));
            var attribute = (PatternTestAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(PatternTestAttribute));

            // MethodReference
            var methodReference =
                methodInfo.DeclaringType.GetTypeDefinition().Methods.FirstOrDefault(m => m.Name == methodInfo.Name);
            const string Message = "An error occured when getting MethodReference of method {0} (type {1}) in RunTestsForAllMethods.";
            Contract.Assume(methodReference != null, string.Format(Message, methodInfo.Name, methodInfo.DeclaringType.Name));

            this.TestChecker(checker, methodReference.Name, pattern.Invoke(methodReference.Resolve()), attribute.ExpectedResult);
        }
    }
}
